/**
 * Author: Fengyuan(Franklin) Zhang
 * Date: 2018/12/25
 * Description: (ho ho ho) Router for model service API
 */
var formidable = require('formidable');
var serviceServer = require('modelservicesdk');
var TaskCtrl = require('../../controls/task');
var ServersCtrl = require('../../controls/servers');
var CommonMethod = require('../../utils/commonMethod');
var Setting = require('../../setting');
var request = require('request');

module.exports = function (app) {

    app.route('/task/rescheduling')
        .get(function (req, res, next) {

            TaskCtrl.reschedulingFunction2();

            res.end("ok");


        });

    // app.route('/task/testTransferOutputUrl')
    //     .get(function (req, res, next) {

    //         var url = "http://175.27.137.60:8082/data/0eb70489-f9bc-43bf-879d-d32ffd9459a5?pwd="
    //         if (url != null && url !== ""){
    //             // 把内网ip换成外网可以访问的地址，供外网下载
    //             // url = url.replace("172.21.212.20:8082", Setting.dataContainerIpAndPort.website);
    //             url = url.replace("221.226.60.2:8082", Setting.dataContainerIpAndPort.website);
    //             url = url.replace("175.27.137.60:8082", Setting.dataContainerIpAndPort.website);
    //         }



    //         res.end("ok:" + url);
    //     });


    app.route('/task/testUpdateStatus')
        .get(function (req, res, next) {

            var taskid = "627771217484482bc42218b1";

            //主动请求manager server的更新任务状态接口 更新manager server task表的状态
            var url = "http://" + Setting.manager.website + "/GeoModeling/task/updateRunTask/" + taskid + "?status=Error";
            request.get(url, function (err, data) {
                if (err){
                    console.log("update manager server runTask status error");
                    res.end("error");
                }
                // {"code":1,"msg":"suc","data":null}
                console.log("update manager server runTask status successfully")

                res.end("ok");

            });

        });


    app.route('/task')
        .post(function (req, res, next) {
            var taskData = req.body;
            var pid = taskData.pid;
            if(pid == undefined || pid == null || pid == ""){
                return res.end(JSON.stringify({
                    result: 'err',
                    code: -3,
                    message: 'No task info!',
                    data: ''
                }));
            }
            ServersCtrl.getByPID(pid, function (err, servers) {
                if (err) {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -3,
                        message: 'No task info!',
                        data: ''
                    }));
                }
                var pid = taskData.pid;
                ServersCtrl.getByPIDWithStatus(pid, true, function (err, servers) {
                    if (err) {
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -1,
                            message: err.message,
                            data: ''
                        }));
                    }
                    if (servers.length > 0) {
                        //! reorder
                        servers = CommonMethod.arrayReorder(servers);
                        // scores = CommonMethod.calculate(servers);

                        TaskCtrl.getAllInitedTasks(function(err, tasks){
                            if(err){
                                return res.end(JSON.stringify({
                                    result: 'err',
                                    code: -4,
                                    message: 'Error in getting all inited tasks',
                                    data: ''
                                }));
                            }
                            
                            //! reorder by wariting task
                            for(var i = 0; i < servers.length; i++){
                                servers[i].initedTask = 0
                            }
                            for(var i = 0; i < tasks.length; i++){
                                for(var j = 0; j < servers.length; j++){
                                    if(tasks[i].t_server == servers[j]._id){
                                        servers[j].initedTask = servers[j].initedTask + 1;
                                        break;
                                    }
                                }
                            }
                            servers.sort(function(a, b){
                                return a.initedTask - b.initedTask;
                            });


                            var server_s = servers.splice(0, 1)[0];

                            // console.log(JSON.stringify(servers));

                            if (typeof taskData.inputs == 'string') {
                                taskData.inputs = JSON.parse(taskData.inputs);
                            }

                            if (typeof taskData.outputs == 'string') {
                                taskData.outputs = JSON.parse(taskData.outputs);
                            }

                            //! insert task record
                            //! output information was appended by MW
                            var task = {
                                t_inputs: taskData.inputs,
                                t_outputs: taskData.outputs,
                                // t_outputs: [],
                                t_user: taskData.username,
                                t_status: 'Inited',
                                t_server: server_s._id,
                                t_msrid: '',
                                t_note: '',
                                t_datetime: new Date(),
                                t_pid: pid,
                                t_type: server_s.s_type
                            };

                            TaskCtrl.add(task, function (err, taskItem) {
                                if (err) {
                                    return res.end(JSON.stringify({
                                        result: 'err',
                                        code: -2,
                                        message: err.message,
                                        data: ''
                                    }));
                                }
                                //! organize parameter
                                var taskinfo = {
                                    "pid": pid,
                                    "taskid": taskItem._id,
                                    "inputs": JSON.stringify(taskData.inputs),
                                    "username": taskData.username,
                                    "ipport": server_s.s_ip + ':' + server_s.s_port,
                                    "outputs": JSON.stringify(taskData.outputs)
                                }

                                if (server_s.s_type == 1) {
                                    //local network
                                    //! post task (主动发起运行任务，相比较私有机器会实现瞬时调用) modified by wangming at 2020.05.24
                                    ServersCtrl.sendTask(server_s, taskinfo, servers, function (err, tdata) {
                                        //! 不需要做任何操作 by MW
                                        if (err) {
                                            return res.end(JSON.stringify({
                                                result: 'err',
                                                code: -3,
                                                message: err.message,
                                                data: ''
                                            }));
                                        }
                                        //! model begin to run, and the task update status and related info
                                        // taskItem.t_server = tdata.server;
                                        taskItem.t_msrid = tdata.msrid;
                                        taskItem.t_status = 'Started';
                                        TaskCtrl.update(taskItem, function (err, data) {
                                            if (err) {
                                                return res.end(JSON.stringify({
                                                    result: 'err',
                                                    code: -4,
                                                    message: err.message,
                                                    data: ''
                                                }));
                                            }
                                            var server = new serviceServer(tdata.server.s_ip, tdata.server.s_port);
                                            var access = server.getServiceAccess();
                                            access.getModelServiceRecordByID(tdata.msrid)
                                                .then(function (record) {
                                                    //! polling : to check status : if 'Error', update status and stop polling; if 'Finished', just stop polling
                                                    var taskPolling = setInterval(() => {
                                                        record.refresh()
                                                            .then(function () {
                                                                var status = record.getStatus();
                                                                if (status == 1) {
                                                                    clearInterval(taskPolling);
                                                                }
                                                                else if (status == -1) {
                                                                    taskItem.t_status = 'Error';
                                                                    //! TODO pick next one
                                                                    //! TODO set as 'Error' not update
                                                                    TaskCtrl.update(taskItem, function (err, data) {
                                                                        if (err) {
                                                                        }
                                                                    });
                                                                    clearInterval(taskPolling);
                                                                }
                                                            });
                                                    }, 600000);
                                                    //! add polling in array and once task finished, polling will be clear
                                                    global.taskPolling.push({
                                                        taskid: taskinfo.taskid,
                                                        polling: taskPolling
                                                    });
                                                });
                                            return res.end(JSON.stringify({
                                                result: 'suc',
                                                code: 1,
                                                message: '',
                                                data: taskItem._id
                                            }));
                                        });
                                    });
                                }
                                else {
                                    //Internet
                                    return res.end(JSON.stringify({
                                        result: 'suc',
                                        code: 1,
                                        message: '',
                                        data: taskItem._id
                                    }));
                                }
                            });
                        });
                    }
                    else {
                        return res.end(JSON.stringify({
                            result: 'suc',
                            code: 0,
                            message: 'No such server!',
                            data: ''
                        }));
                    }
                });
            });
        });


    app.route('/task/:id')
        .get(function (req, res, next) {
            TaskCtrl.getByOID(req.params.id, function (err, task) {
                if (err) {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: err.message,
                        data: ''
                    }));
                }
                return res.end(JSON.stringify({
                    result: 'suc',
                    code: 1,
                    message: '',
                    data: task
                }));
            });
        })
        .put(function (req, res, next) {
            var taskid = req.params.id;
            var req_status = 'Unknown';
            var req_note = "";
            if(req.body.t_note){
                // req_note = req.body.t_note;
            }
            try {
                req_status = parseInt(req.body.status);
            }
            catch (ex) {
                //! Error in parsing parameter
                return res.end(JSON.stringify({
                    result: 'err',
                    code: -1,
                    message: 'Parameter error!',
                    data: 'status'
                }));
            }
            //TODO 2020.01.05 by MW 因为模型容器错误返回了状态2 来代表Started状态，导致此处存在误解
            if (req_status == 2) {  //update model status to Started, Started: 2, Finished: 1, Inited: 0, Error: -1
                var msrid = 'Unknown';
                try {
                    msrid = req.body.msrid;
                } catch (ex) {
                    //TODO Handle Error
                    console.log(ex);
                }
                //Do not need to check the ip
                TaskCtrl.getByOID(taskid, function (err, task) {
                    if (err) {
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -1,
                            message: err.message,
                            data: ''
                        }));
                    }
                    if(task == null){
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -1,
                            message: 'No such task',
                            data: ''
                        }));
                    }
                    task.t_status = 'Started';
                    task.t_msrid = msrid;
                    TaskCtrl.update(task, function (err, result) {
                        if (err) {
                            return res.end(JSON.stringify({
                                result: 'err',
                                code: -1,
                                message: err.message,
                                data: ''
                            }));
                        }

                        updateRunTask(taskid,task.t_status);

                        //! Remove it by FZ at 2020-6-24, because tasks in type 1, 2 (local network version and internet version) can be updated by model service container.
			            // if (task.t_type == 1) {
                        //     //说明是同一个网段内的机器，那么调用模型服务sdk，从而更新当容器因宕机出现的模型运行出错问题
                        //     ServersCtrl.getByOID(task.t_server, function (err, tdata) {
                        //         if (err) {

                        //         }
                        //         var server = new serviceServer(tdata.s_ip, tdata.s_port);
                        //         var access = server.getServiceAccess();
                        //         access.getModelServiceRecordByID(task.t_msrid)
                        //             .then(function (record) {
                        //                 //! polling : to check status : if 'Error', update status and stop polling; if 'Finished', just stop polling
                        //                 var taskPolling = setInterval(() => {
                        //                     record.refresh()
                        //                         .then(function () {
                        //                             var status = record.getStatus();
                        //                             if (status == 1) {
                        //                                 clearInterval(taskPolling);
                        //                             }
                        //                             else if (status == -1) {
                        //                                 task.t_status = 'Error';
                        //                                 //! TODO pick next one
                        //                                 //! TODO set as 'Error' not update
                        //                                 TaskCtrl.update(task, function (err, data) {
                        //                                     if (err) {
                        //                                     }
                        //                                 });
                        //                                 clearInterval(taskPolling);
                        //                             }
                        //                         })
                        //                         .catch(err => {
                        //                             task.t_status = 'Error';
                        //                             TaskCtrl.update(task, function (err, data) {
                        //                                 if (err) {
                        //                                 }
                        //                             });
                        //                             clearInterval(taskPolling);
                        //                         })
                        //                 }, 1000);
                        //                 //! add polling in array and once task finished, polling will be clear
                        //                 global.taskPolling.push({
                        //                     taskid: task._id,
                        //                     polling: taskPolling
                        //                 });
                        //             })
                        //             .catch(function (err) {
                        //                 taskItem.t_status = 'Error';
                        //                 TaskCtrl.update(taskItem, function (err, data) {
                        //                     if (err) {
                        //                     }
                        //                 });
                        //                 clearInterval(taskPolling);
                        //             });
                        //     })

                        //     return res.end(JSON.stringify({
                        //         result: 'suc',
                        //         code: 1,
                        //         message: 'change status success',
                        //         data: ''
                        //     }));
                        // }
                        return res.end(JSON.stringify({
                            result: 'suc',
                            code: 1,
                            message: 'change status success',
                            data: ''
                        }));
                    });
                });
            } else {
                var ip = CommonMethod.getIP(req);
                var mac = 'Unknown';
                var msrid = 'Unknown';
                try {
                    mac = req.body.mac;
                }
                catch (ex) {
                    //! Error in parsing parameter
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: 'Parameter error!',
                        data: 'mac_address'
                    }));
                }

                var req_outputs = [];
                try {
                    req_outputs = req.body.outputs;
                }
                catch (ex) {
                    //! Error in parsing parameter
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: 'Parameter error!',
                        data: 'output_data'
                    }));
                }
                try {
                    msrid = req.body.msrid;
                } catch (ex) {
                    //! Error in parsing parameter
                    console.log(ex);
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: 'Parameter error!',
                        data: 'model_record_id'
                    }));
                }
                ServersCtrl.checkIP(ip, mac, function (err, server) {
                    if (err) {
                        //! Error in parsing parameter
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -1,
                            message: 'Parameter error!',
                            data: 'mac_address'
                        }));
                    }
                    if (server == false) {
                        //! Error in get server
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -2,
                            message: 'Error in matching server: Can not find server record!',
                            data: ''
                        }));
                    }
                    else {
                        TaskCtrl.getByOID(taskid, function (err, task) {
                            if (err) {
                                //! Error in get server
                                return res.end(JSON.stringify({
                                    result: 'err',
                                    code: -2,
                                    message: 'Error in matching task record!',
                                    data: ''
                                }));
                            }
                            if(task == null){
                                return res.end(JSON.stringify({
                                    result: 'err',
                                    code: -1,
                                    message: 'No such task',
                                    data: ''
                                }));
                            }
                            var status = 'Started';
                            if (req_status == 1) {
                                status = 'Finished';
                                task.t_outputs = JSON.parse(req_outputs);
                                // console.log(req_outputs);

                                //输出地址统一转换成'geomodeling.njnu.edu.cn/dataTransferServer'
                                let outputs = task.t_outputs;
                                for (let i = 0; i < outputs.length; i++) {
                                    let url = outputs[i].Url
                                    if (url != null && url !== ""){
                                        // 把内网ip换成外网可以访问的地址，供外网下载
                                        // url = url.replace("172.21.212.20:8082", Setting.dataContainerIpAndPort.website);
                                        url = url.replace("221.226.60.2:8082", Setting.dataContainerIpAndPort.website);
                                        url = url.replace("175.27.137.60:8082", Setting.dataContainerIpAndPort.website);
                                        outputs[i].Url = url;
                                    }
                                }
                                task.t_outputs = outputs;

                            }
                            else if (req_status == -1) {
                                task.t_note = req_note;
                                status = 'Error';
                            } else{
                                status = 'Error';
                            }
                            task.t_status = status;
                            TaskCtrl.update(task, function (err, result) {
                                if (err) {
                                    //! Error in updating task message
                                    return res.end(JSON.stringify({
                                        result: 'err',
                                        code: -3,
                                        message: err.message,
                                        data: ''
                                    }));
                                }

                                updateRunTask(taskid,task.t_status);

                                return res.end(JSON.stringify({
                                    result: 'suc',
                                    code: 1,
                                    message: '',
                                    data: ''
                                }));
                            });
                            if (server.s_type == 1) {
                                for (var i = 0; i < global.taskPolling.length; i++) {
                                    if (task._id == global.taskPolling[i].taskid) {
                                        clearInterval(global.taskPolling[i].polling);
                                    }
                                }
                            }
                        });
                    }
                });
            }



        });


    function updateRunTask(taskid, status) {
        //主动请求manager server的更新任务状态接口 更新manager server task表的状态
        var url = "http://" + Setting.manager.website + "/GeoModeling/task/updateRunTask/" + taskid + "?status=" + status;
        request.get(url, function (err, data) {
            if (err){
                console.log("update manager server runTask status error");
                return;
            }
            // {"code":1,"msg":"suc","data":null}
            console.log("update manager server runTask status successfully")

        });
    }

    //get the task list which should be invoked(Inited task, only get the task t_type = 2 )
    //突然发现这里实现逻辑过于复杂了，私有服务器ip基本上都是走代理的，不应该从ip层来判断，直接判断mac地址
    //! the problem above has been solved by FZ at 2020-6-27
    //! Now, all the request by mac address
    app.route('/task/inited/all')
        .get(function (req, res, next) {
            // var ip = CommonMethod.getIP(req);
            var mac = req.query.mac;
            var status = JSON.parse(decodeURI(req.query.status));
            status.cpuInfo = parseInt(status.cpuInfo*100) + "%";
            ServersCtrl.getByMac(mac, function (err, servers) {
                if (err) {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: err.message,
                        data: []
                    }));
                }
                if (servers.length > 0) {
                    var server = servers[0];
                    var server_id = server._id;
                    server.s_datetime = new Date();
                    server.s_status = true;
                    server.s_hardware = status;

                    //! update the server time in order to judge the server connection status
                    ServersCtrl.update(server, function (err, result) { });


                    TaskCtrl.getByServerAndInitdStatus(server_id, function (err, tasks) {
                        if (err) {
                            return res.end(JSON.stringify({
                                result: 'err',
                                code: -1,
                                message: err.message,
                                data: []
                            }));
                        }


                        //! reverse order the tasks
                        tasks.reverse();

                        //! check the number of running instance, only send “suitable” number of tasks
                        //! current we only allow one computing has one running task
                        let allowTaskNum = 5;
                        if(tasks.length > 0){
                            if(status.runningIns == undefined){
                                console.log("Return 5 tasks (No running status got) - IP:[" + server.s_ip + "]");
                                return res.end(JSON.stringify({
                                    result: 'suc',
                                    code: 1,
                                    message: '',
                                    data: tasks.slice(0, allowTaskNum)
                                }));
                            }
                            else if(status.runningIns == 0){
                                console.log("Return 5 tasks - IP:[" + server.s_ip + "]");
                                return res.end(JSON.stringify({
                                    result: 'suc',
                                    code: 1,
                                    message: '',
                                    // data: tasks
                                    data: tasks.slice(0, allowTaskNum)
                                }));
                            }
                            else if(status.runningIns > 0 && status.runningIns < allowTaskNum){
                                console.log("Return " + (allowTaskNum - status.runningIns).toString() + " tasks (" + status.runningIns.toString() + " instances are running in the container) - IP:[" + server.s_ip + "]");
                                return res.end(JSON.stringify({
                                    result: 'suc',
                                    code: 1,
                                    message: '',
                                    data: tasks.splice(allowTaskNum - status.runningIns)
                                }));
                            }
                            else if(status.runningIns > allowTaskNum - 1){
                                console.log("None task returned!(5 instances are running in the container) - IP:[" + server.s_ip + "]");
                                return res.end(JSON.stringify({
                                    result: 'suc',
                                    code: 1,
                                    message: '',
                                    data: []
                                }));
                            }
                        }
                        else{
                            console.log("None task returned!");
                            return res.end(JSON.stringify({
                                result: 'suc',
                                code: 1,
                                message: '',
                                data: []
                            }));
                        }
                        
                    })

                } else {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: 'Database error, no such record',
                        data: []
                    }));
                    // it indicates that the server's ip may have changed!
                    // ServersCtrl.checkIP(ip, mac, function (err, result) {
                    //     if (err) {
                    //         return res.end(JSON.stringify({
                    //             result: 'err',
                    //             code: -1,
                    //             message: err.message,
                    //             data: []
                    //         }));
                    //     }
                    //     if (!result) {
                    //         //Database error, no such record
                    //         return res.end(JSON.stringify({
                    //             result: 'err',
                    //             code: -1,
                    //             message: 'Database error, no such record',
                    //             data: []
                    //         }));
                    //     } else {
                    //         //update success, start request
                    //         var server_id = result._id;
                    //         TaskCtrl.getByServerAndInitdStatus(server_id, function (err, tasks) {
                    //             if (err) {
                    //                 return res.end(JSON.stringify({
                    //                     result: 'err',
                    //                     code: -1,
                    //                     message: err.message,
                    //                     data: []
                    //                 }));
                    //             }
                    //             return res.end(JSON.stringify({
                    //                 result: 'suc',
                    //                 code: 1,
                    //                 message: '',
                    //                 data: tasks
                    //             }));
                    //         })
                    //     }
                    // })
                }
            })
        })
    // 根据sid将模型运算任务分发到特定的模型容器上进行运算
    app.route('/task/invoke/:sid')
        .post(function (req, res, next) {
            var sid = req.params.sid;
            var taskData = req.body;
            ServersCtrl.getByOID(sid, function (err, server) {
                if (err) {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: err.message,
                        data: ''
                    }));
                }
                if (server == null) {
                    return res.end(JSON.stringify({
                        result: 'err',
                        code: -1,
                        message: 'no this computer resource',
                        data: ''
                    }));
                }

                if (typeof taskData.inputs == 'string') {
                    taskData.inputs = JSON.parse(taskData.inputs);
                }

                if (typeof taskData.outputs == 'string') {
                    taskData.outputs = JSON.parse(taskData.outputs);
                }

                var task = {
                    t_inputs: taskData.inputs,
                    t_outputs: taskData.outputs,
                    t_user: taskData.username,
                    t_status: 'Inited',
                    t_server: server._id,
                    t_msrid: '',
                    t_note: '',
                    t_datetime: new Date(),
                    t_pid: taskData.pid,
                    t_type: server.s_type
                };

                TaskCtrl.add(task, function (err, taskItem) {
                    if (err) {
                        return res.end(JSON.stringify({
                            result: 'err',
                            code: -2,
                            message: err.message,
                            data: ''
                        }));
                    }
                    //! organize parameter
                    var taskinfo = {
                        "pid": taskData.pid,
                        "taskid": taskItem._id,
                        "inputs": JSON.stringify(taskData.inputs),
                        "username": taskData.username,
                        "ipport": server.s_ip + ':' + server.s_port,
                        "outputs": JSON.stringify(taskData.outputs)
                    }
                    if (server.s_type == 1) {
                        ServersCtrl.sendTask(server, taskinfo, [], function (err, tdata) {
                            //不需要做任何操作
                            if (err) {
                                console.log(err);
                            }
                            console.log(tdata);
                        });
                        return res.end(JSON.stringify({
                            result: 'suc',
                            code: 1,
                            message: '',
                            data: taskItem._id
                        }));
                    } else {
                        //Internet 
                        return res.end(JSON.stringify({
                            result: 'suc',
                            code: 1,
                            message: '',
                            data: taskItem._id
                        }));
                    }
                });

            })
        })

    app.route("/test")
        .get((req, res, next) => {

            // for (let i = 0; i < 100; i++) {
            //     console.log(Math.round(Math.random()*10));
            // }


            TaskCtrl.reschedulingFunction2();
            return res.end(JSON.stringify({
                result: 'suc',
                code: 1,
                message: '',
                data: ''
            }));


            // var time1 = new Date("2021-9-16");
            // setInterval(() => {
            //     var time2 = new Date();
            //     var interval = time2 - time1;
            //     return res.end(JSON.stringify({
            //         result: 'suc',
            //         code: 1,
            //         message: '',
            //         data: (interval / 1000 / 60) > (23 * 60)
            //     }));
            // },500);




    });

}